package org.keyyond.ms.gateway.filter;

import java.io.BufferedReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.keyyond.ms.core.util.NetworkUtil;
import org.keyyond.ms.core.util.StringUtil;
import org.keyyond.ms.gateway.model.MsAccessConfig;
import org.keyyond.ms.gateway.service.IMsAccessConfigService;
import org.keyyond.ms.gateway.util.AppUitls;
import org.keyyond.ms.gateway.util.MsCustomerRequestWrapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.netflix.zuul.filters.pre.FormBodyWrapperFilter;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import org.springframework.web.util.WebUtils;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.http.HttpServletRequestWrapper;

public class MsPreRequestLogFilter extends ZuulFilter {
	private static Logger log = LoggerFactory
			.getLogger(MsPreRequestLogFilter.class);

	@Autowired
	private IMsAccessConfigService keyyondAccessConfigService;

	// @Autowired
	// private MultipartResolver multipartResolver;

	private final static String CONST_HEADER_REQUEST_SOURCE = "REQUEST_SOURCE";
	private final static String CONST_PARAMETER_ACCESSTOKEN = "accessToken";
	private final static String CONST_PARAMETER_SECRETID = "secretId";

	private final static String CONST_HEADER_REQUEST_SOURCE_VALUE = "FRONT";
	private final static String CONST_REQUEST_METHOD_OPTIONS = "options";

	private final static String CONST_ERROR_RETURN_MESSAGE_REQUEST_SIZE = "{\"result\":false,\"errcode\":\"4002\",\"errmsg\":\"文件太大!\"}";
	private final static String CONST_ERROR_RETURN_MESSAGE = "{\"result\":false,\"errcode\":\"1000\",\"errmsg\":\"您没有权限!\"}";
	private static int CONST_MAX_REQUEST_SIZE = 100 * 1024 * 1024;// 限流10M

	private final static String CONST_STATIC_FILE_HTML = ".html";
	private final static String CONST_STATIC_FILE_JS = ".js";
	private final static String CONST_STATIC_FILE_CSS = ".css";
	private final static String CONST_STATIC_FILE_PNG = ".png";
	private final static String CONST_STATIC_FILE_WOFF2 = ".woff2";
	private final static String CONST_STATIC_FILE_WOFF = ".woff";
	private final static String CONST_STATIC_FILE_TTF = ".ttf";
	private final static String CONST_STATIC_FILE_GIF = ".gif";
	private final static String CONST_STATIC_FILE_SVG = ".svg";
	private final static String CONST_WEBSERVICE_URL = "webservice/DCP/SendInterface";


	private final static String CONST_COMPANY_KEY = "company_key";
	private final static String CONST_COMPANY_SECRET = "company_secret";
	private final static String CONST_SIGN = "sign";
	private final static String CONST_RADOM_STR = "radom_str";
	private final static String CONST_WX_NSUKEY = "nsukey";
	private final static String CONST_WX_FROM = "from";
	private final static String CONST_WX_ISAPPINSTALLED = "isappinstalled";

	@Override
	public Object run() {
		RequestContext ctx = RequestContext.getCurrentContext();
		// copy and set a requestWrapper to RequestContext , to ensure the next
		// ms can read stream from request
		HttpServletRequest request = ctx.getRequest();
		if (request.getContentLength() > CONST_MAX_REQUEST_SIZE)// 限流
		{
			ctx.setSendZuulResponse(false);
			ctx.setResponseStatusCode(500);
			ctx.set("isSuccess", false);
			ctx.setResponseBody(CONST_ERROR_RETURN_MESSAGE_REQUEST_SIZE);
			return null;
		}

		log.debug("this is a pre filter:" + request.getMethod()
				+ request.getRequestURL().toString());
		String ipaddress = null;
		try {
			ipaddress = NetworkUtil.getIpAddress(request);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		int result = 0;
		String url = null;
		// String accessToken = null;
		// String secretId = null;
		// String requestSource = null;
		String company_key = null;
		String sign = null;
		String radom_str = null;

		Map<String, String> parameterMap = new HashMap<String, String>();

		String mediaType = request.getContentType();
		// 文件上传时，复制request的流
		if (!StringUtils.isEmpty(mediaType)
				&& mediaType.contains("multipart/form-data")) {
			MsCustomerRequestWrapper wrapper = null;
			try {
				wrapper = new MsCustomerRequestWrapper(request);
				ctx.setRequest(wrapper);
				parameterMap = readParamByRequest(wrapper);
				// url = request.getRequestURL().toString();
				// accessToken =
				// String.valueOf(parameterMap.get(CONST_PARAMETER_ACCESSTOKEN));
				// secretId =
				// String.valueOf(parameterMap.get(CONST_PARAMETER_SECRETID));
				// requestSource =
				// String.valueOf(parameterMap.get(CONST_HEADER_REQUEST_SOURCE));

				url = request.getRequestURL().toString();
				company_key = String.valueOf(parameterMap
						.get(CONST_COMPANY_KEY));
				sign = String.valueOf(parameterMap.get(CONST_SIGN));
				radom_str = String.valueOf(parameterMap.get(CONST_RADOM_STR));

			} catch (IOException e1) {
				// TODO Auto-generated catch block
				log.info("multipart/form-data type, param parsing exception");
				e1.printStackTrace();
				ctx.setSendZuulResponse(false);
				ctx.setResponseStatusCode(401);
				ctx.set("isSuccess", false);
				ctx.setResponseBody(CONST_ERROR_RETURN_MESSAGE);
				return null;
			}

			//
			log.info("i'm multipart/form-data type, company_key:" + company_key
					+ " || sign:" + sign + " || radom_str:" + radom_str);
		} else {
			try{
				url = request.getRequestURL().toString();
				company_key = request.getParameter(CONST_COMPANY_KEY);
				sign = request.getParameter(CONST_SIGN);
				radom_str = request.getParameter(CONST_RADOM_STR);

				Enumeration<String> e = request.getParameterNames();
				while (e.hasMoreElements()) {
					String name = e.nextElement();
					String str = request.getParameter(name);
					name = name.replace("[", "").replace("]", "");
					parameterMap.put(name, str);
				}
			}catch (Exception e) {
				// TODO: handle exception
				e.printStackTrace();
				ctx.setSendZuulResponse(false);
				ctx.setResponseStatusCode(401);
				ctx.set("isSuccess", false);
				ctx.setResponseBody(CONST_ERROR_RETURN_MESSAGE);
				return null;
			}

		}

		log.info("======IP:=====================" + ipaddress);
		log.info("======url:=====================" + url);
		log.info("======company_key:=====================" + company_key);
		log.info("======sign:=====================" + sign);
		log.info("======radom_str:=====================" + radom_str);
		log.info("======traceID:====================="
				+ request.getHeader("X-B3-TraceId"));
		log.info("======spanID:====================="
				+ request.getHeader("X-B3-SpanId"));

		// opinion类型的请求直接pass
		if (CONST_REQUEST_METHOD_OPTIONS.equals(request.getMethod()
				.toLowerCase())) {
			log.info("Option request pass");
			ctx.setSendZuulResponse(true);
			ctx.setResponseStatusCode(200);
			ctx.set("isSuccess", true);
			return null;
		}
		// 静态文件不控制权限
		if (!StringUtils.isEmpty(url)
				&& (//url.endsWith(CONST_STATIC_FILE_HTML)||  //html需要控制访问权限
						 url.endsWith(CONST_STATIC_FILE_JS)||
						 url.endsWith(CONST_STATIC_FILE_CSS) ||
						 url.endsWith(CONST_STATIC_FILE_TTF) ||
						 url.endsWith(CONST_STATIC_FILE_WOFF) ||
						 url.endsWith(CONST_STATIC_FILE_WOFF2) ||
						 url.endsWith(CONST_STATIC_FILE_GIF) ||
						 url.endsWith("My97DatePicker.htm") ||
						 url.endsWith(CONST_STATIC_FILE_SVG) ||
						 url.contains(CONST_WEBSERVICE_URL) ||
						 url.endsWith(CONST_STATIC_FILE_PNG))) {
			log.info("request static files pass");
			ctx.setSendZuulResponse(true);
			ctx.setResponseStatusCode(200);
			ctx.set("isSuccess", true);
			return null;
		}

		// 先判断有没有权限访问
//		if (!StringUtils.isEmpty(company_key) && !StringUtils.isEmpty(url)
//				&& !StringUtils.isEmpty(sign)
//				&& !StringUtils.isEmpty(radom_str)) {
		if (!StringUtils.isEmpty(url)){
			MsAccessConfig mac = new MsAccessConfig();
			mac.setServiceurl(url);
			mac.setAccesstoken(company_key);
			mac = keyyondAccessConfigService.getAccessConfig(mac);
			if (mac == null)// 没有权限直接返回401
			{
				log.info("no permition request, not pass");
				ctx.setSendZuulResponse(false);
				ctx.setResponseStatusCode(401);
				ctx.set("isSuccess", false);
				ctx.setResponseBody(CONST_ERROR_RETURN_MESSAGE);
				return null;
			} else {
				// 判断签名是否正确
//				if (checkSign(mac, sign, radom_str, parameterMap))// 验证通过
//				{
					log.info("sign checking pass, goto next service!!");
					ctx.setSendZuulResponse(true);
					ctx.setResponseStatusCode(200);
					ctx.set("isSuccess", true);
					return null;
//				} else {
//					log.info("sign checking fail, return 401!!");
//					ctx.setSendZuulResponse(false);
//					ctx.setResponseStatusCode(401);
//					ctx.set("isSuccess", false);
//					ctx.setResponseBody(CONST_ERROR_RETURN_MESSAGE);
//					return null;
//				}
			}
		}

		ctx.setSendZuulResponse(false);
		ctx.setResponseStatusCode(401);
		ctx.set("isSuccess", false);
		ctx.setResponseBody(CONST_ERROR_RETURN_MESSAGE);
		return null;

	}

	@Override
	public boolean shouldFilter() {
		return true;
	}

	@Override
	public int filterOrder() {
		return 1000;
	}

	@Override
	public String filterType() {
		return FilterConstants.PRE_TYPE;
	}

	private Map readParamByRequest(MsCustomerRequestWrapper wrapper)
			throws IOException {
		Map result = new HashMap();
		BufferedReader br = wrapper.getReader();
		String s = null;
		StringBuffer sb = new StringBuffer();
		while ((s = br.readLine()) != null) {
			// log.info("request stream line :" + s);
			if (!"".equals(StringUtil.isNullString(s))
					&& s.contains("form-data")) {
				String name = s.substring(s.indexOf("name=") + 5, s.length());
				s = br.readLine();
				s = br.readLine();
				if (!"".equals(StringUtil.isNullString(s))) {
					// log.info("request parameter key:" + name + ",value:" +
					// s);
					result.put(name.replaceAll("\"", ""), s);
				}
			}
		}

		br.close();
		// TODO:抓取parameter
		return result;
	}

	/**
	 * 生成签名
	 *
	 * @param map
	 * @return
	 */
	public static boolean checkSign(MsAccessConfig mac, String sign,
			String radom_str, Map<String, String> map) {

		String result = "";
		try {
			map.put(CONST_COMPANY_KEY, mac.getAccesstoken());
			map.put(CONST_RADOM_STR, radom_str);
			List<Map.Entry<String, String>> infoIds = new ArrayList<Map.Entry<String, String>>(map.entrySet());
			// 对所有传入参数按照字段名的 ASCII 码从小到大排序（字典序）
			Collections.sort(infoIds, new Comparator<Map.Entry<String, String>>() {

				public int compare(Map.Entry<String, String> o1, Map.Entry<String, String> o2) {
					return (o1.getKey()).toString().compareTo(
							o2.getKey());
				}
			});

			StringBuffer sb = new StringBuffer("");
			for (Map.Entry<String, String> item : infoIds) {
				if (item.getKey() != null && item.getKey() != "") {
					String key = item.getKey();
					String val = item.getValue();
					if (!(val == "" || val == null) && !CONST_SIGN.equals(key)
							&& !CONST_WX_NSUKEY.equals(key)
							&& !(CONST_WX_FROM.equals(key) && "singlemessage".equals(val))
							&& !CONST_WX_ISAPPINSTALLED.equals(key)
							) {
						sb.append(key + "=" + val + "&");
					}
				}

			}

			sb.append(CONST_COMPANY_SECRET).append("=").append(mac.getSecretid());
			// sb.append(PropertyManager.getProperty("SIGNKEY"));
			result = sb.toString();
			log.info("sign strA is =================" + result);
			// 进行MD5加密
			result = AppUitls.generateSign(result).toUpperCase();
			log.info("generated sign from strA is =================" + result);

			if (sign.toUpperCase().equals(result)) {
				return true;
			} else {
				return false;
			}
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}
}
